package net.gdface.json;

import static net.gdface.utils.DateSupport.castToDate;
import static net.gdface.utils.DateSupport.parseAsDate;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;

import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.sql.Timestamp;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.google.common.base.Function;

import net.gdface.cache.FunctionCached;

/**
 * 自适应日期类型解析器实现,支持更多日期格式
 * @author guyadong
 * @since 2.8.14
 */
public class JacksonDateDeserializers {

	private static final FunctionCached<Class<?>,JsonDeserializer<?> > deserializerCache = FunctionCached.of(new Function<Class<?>,JsonDeserializer<?>>() {

		@Override
		public JsonDeserializer<?> apply(Class<?> input) {
			JsonDeserializer<?> deserializer = find(checkNotNull(input,"input is null"));
			checkArgument(null != deserializer,"UNSUPPORTED date type "+ input.getName());
			return deserializer;
		}
	});
	@SuppressWarnings("unchecked")
	public static<T> JsonDeserializer<T> findCached(Class<T> rawType){
		return (JsonDeserializer<T>) deserializerCache.get(rawType);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T>JsonDeserializer<T> find(Class<T> rawType)
	{
		JsonDeserializer deserializer = null;
		if(null != rawType) {
			if (rawType == Calendar.class) {
				deserializer = CalendarDeserializer.INSTANCE;
			}
			if (rawType == java.util.Date.class) {
				deserializer = DateDeserializer.INSTANCE;
			}
			if (rawType == java.sql.Date.class) {
				deserializer = SqlDateDeserializer.INSTANCE;
			}
			if (rawType == Timestamp.class) {
				deserializer = TimestampDeserializer.INSTANCE;
			}
			if (rawType == GregorianCalendar.class) {
				deserializer = CalendarDeserializer.INSTANCE_GREGORIAN;
			}
			if (Date.class.isAssignableFrom(rawType) || Calendar.class.isAssignableFrom(rawType)) {
				deserializer = new BaseDateDeserializer(rawType);
			}
		}
		return deserializer;
	}
	/**
	 * 自适应日期解析实现类
	 * @author guyadong
	 *
	 * @param <T> 日期类型，要求为java.util.Date或 Calendar或其子类
	 */
	public static class BaseDateDeserializer<T> extends StdDeserializer<T> {

		private static final long serialVersionUID = 3686273235838778015L;

		public BaseDateDeserializer(Class<? extends T> valueClass) {
			super(valueClass);
			/** 参数类型检查:要求为java.util.Date或 Calendar或其子类 */
			checkArgument(
					null != valueClass 
					&& (Date.class.isAssignableFrom(valueClass) || Calendar.class.isAssignableFrom(valueClass)),
					"valueClass required Date or Calendar");
		}

		/**
		 * 重写父类方法，先调用父类方法，调用失败时调用用 parseAsDate 自适应解析
		 */
		@Override
		protected Date _parseDate(String value, DeserializationContext ctxt) throws IOException {
			try {
				return super._parseDate(value, ctxt);
			} catch (Exception e) {
				/** 尝试使用更多的日期格式解析字符串，解析失败抛出原异常 */
				Date date = parseAsDate(value, Date.class);
				if(date != null) {
					return date;
				}
				throw e;
			}
		}

		@SuppressWarnings("unchecked")
		@Override
		public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
			return (T) castToDate(_parseDate(jp, ctxt),handledType());
		}
	}
	public static class CalendarDeserializer extends BaseDateDeserializer<Calendar>{
		private static final long serialVersionUID = -7339485494738370791L;
		public static final CalendarDeserializer INSTANCE = new CalendarDeserializer();
		public static final CalendarDeserializer INSTANCE_GREGORIAN = new CalendarDeserializer(GregorianCalendar.class);
		public CalendarDeserializer(Class<? extends Calendar> type) {
			super(type);
		}
		public CalendarDeserializer() {
			super(Calendar.class);
		}
	}
	public static class DateDeserializer extends BaseDateDeserializer<Date>{
		private static final long serialVersionUID = -5127256248213713453L;
		public static final DateDeserializer INSTANCE = new DateDeserializer();
		public DateDeserializer() {
			super(Date.class);
		}

	}
	public static class SqlDateDeserializer extends BaseDateDeserializer<java.sql.Date>{
		private static final long serialVersionUID = 4291873951254469229L;
		public static final SqlDateDeserializer INSTANCE = new SqlDateDeserializer();
		public SqlDateDeserializer() {
			super(java.sql.Date.class);
		}

	}
	public static class TimestampDeserializer extends BaseDateDeserializer<Timestamp>{
		private static final long serialVersionUID = 629505128121672813L;
		public static final TimestampDeserializer INSTANCE = new TimestampDeserializer();
		public TimestampDeserializer() {
			super(Timestamp.class);
		}

	}
}
